using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Design;
using System.Drawing.Drawing2D;

namespace Blimpbots.Recognition
{
    public partial class PictureBox2 : UserControl
    {
        public class InterestPoint
        {
            public Point Location;
            public bool Selected;
            public float Size;
            public object Tag;
            public InterestPoint()
            {
                Size = 4.0f; Tag = null;
            }
        }

        private Dictionary<string, Point> _namedPoints = new Dictionary<string, Point>();
        public Dictionary<string, Point> NamedPoints
        {
            get { return _namedPoints; }
            set { _namedPoints = value; }
        }


        public List<Point> Outline
        {
            get { return _outline; }
            set { _outline = value; }
        }

        public string SelectedNamedPoint;

        private List<Point> _outline = new List<Point>();

        private List<InterestPoint> ipoints;

        public List<InterestPoint> InterestPoints 
        {
            get { return ipoints; }
            set { ipoints = value; }
        }

        Brush leftBrush, rightBrush;

        Point startPt;
        Point lastLocation;
        Rectangle selectionRect;
        private bool isDragging = false;
        MouseButtons dragButton;

        public enum EditMode
        {
            RegionEdit,
            OutlineDraw,
            SelectPoint
        }

        private EditMode _editMode;

        public EditMode Mode {
            set {
                _editMode = value;
                isDragging = false; // terminate any current drag
                this.Invalidate();
            }
            get
            {
                return _editMode;
            }
        }

        private Image scene;
       
        [Description("Scene to show behind the points."), Category("Appearance")]
        public Image Scene { set { scene = value; Invalidate(); } get { return scene; } }

        public PictureBox2()
        {
            InitializeComponent();
            this.SetStyle( ControlStyles.AllPaintingInWmPaint | 
                            ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true);

            leftBrush = new SolidBrush(Color.FromArgb(128, Color.Green));
            rightBrush = new SolidBrush(Color.FromArgb(128, Color.Red));

            _editMode = EditMode.RegionEdit;
        }

        private void PictureBox2_MouseDown(object sender, MouseEventArgs e)
        {
            if (_editMode == EditMode.RegionEdit)
            {
                // Set whichever button initiates the drag
                if (!isDragging)
                {
                    startPt = e.Location;
                    lastLocation = startPt;
                    selectionRect = MakeRect(startPt, startPt, startPt);
                    dragButton = e.Button;
                    isDragging = true;
                    this.Invalidate();
                }
            }
            else if (_editMode == EditMode.OutlineDraw)
            {
                _outline.Add(e.Location);
                this.Invalidate();
            }
            else if (_editMode == EditMode.SelectPoint)
            {
                // Set the named point (creates it if it doesn't exist)
                _namedPoints[SelectedNamedPoint] =
                    e.Location;

                // Return to region edit mode
                this.Mode = EditMode.RegionEdit;

                this.Invalidate();
            }

        }

        private void PictureBox2_Paint(object sender, PaintEventArgs e)
        {
            if(scene != null)
                e.Graphics.DrawImage(scene, e.ClipRectangle, e.ClipRectangle, GraphicsUnit.Pixel);

            foreach (KeyValuePair<string, Point> kvp in _namedPoints)
            {
                Rectangle ptRect = new Rectangle(kvp.Value.X - 2,
                                                 kvp.Value.Y - 2,
                                                 4, 4);

                if (kvp.Key == SelectedNamedPoint)
                {
                    Rectangle bigPt = ptRect; bigPt.Inflate(2, 2);
                    e.Graphics.DrawEllipse(Pens.Gold, bigPt);
                }
                else
                    e.Graphics.DrawEllipse(Pens.Gold, ptRect);
            }

            if(_outline.Count > 0)
            {
                Point last = _outline[0];
                for(int i = 1; i < _outline.Count; i++)
                {
                    Point cur = _outline[i];
                    e.Graphics.DrawLine(Pens.White, last, cur);
                    last = cur;
                }

                // Draw the closing line in gray
                e.Graphics.DrawLine(Pens.Gray, last, _outline[0]);
            }

            Brush b = (dragButton == MouseButtons.Left) ? leftBrush : rightBrush;
            bool deSelecting = dragButton == MouseButtons.Right;

            if (isDragging)
            {
                e.Graphics.FillRectangle(b, selectionRect);
            }

            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            if (ipoints != null)
            {
                Rectangle window = e.ClipRectangle;
                window.Inflate(4,4);

                foreach (InterestPoint p in ipoints)
                {
                    if (window.Contains(p.Location))
                    {
                        if ( p.Selected && !(isDragging && selectionRect.Contains(p.Location )) ||
                            isDragging && !deSelecting && selectionRect.Contains(p.Location))
                            e.Graphics.FillEllipse(Brushes.Chartreuse,
                                (float)p.Location.X - p.Size / 2, (float)p.Location.Y - p.Size / 2,
                                p.Size, p.Size);
                        else
                        {                            
                            e.Graphics.FillEllipse(Brushes.HotPink,
                                (float)p.Location.X - p.Size / 2, (float)p.Location.Y - p.Size / 2,
                                p.Size, p.Size);
                        }
                    }
                }
            }

            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.Default;


        }

        private void PictureBox2_MouseUp(object sender, MouseEventArgs e)
        {
            if (_editMode == EditMode.RegionEdit)
            {
                if (isDragging && e.Button == dragButton)
                {
                    UpdateSelection(dragButton == MouseButtons.Right, selectionRect);
                    isDragging = false;
                    this.Invalidate();
                }
            }
        }

        private void UpdateSelection(bool deSelect, Rectangle selectionRect)
        {
            if (ipoints == null) return;
            foreach(InterestPoint p in ipoints)
            {
                if (selectionRect.Contains(p.Location))
                    p.Selected = !deSelect;
            }
        }

        private void PictureBox2_MouseMove(object sender, MouseEventArgs e)
        {
            if (_editMode == EditMode.RegionEdit)
            {
                if (isDragging)
                    selectionRect = MakeRect(startPt, e.Location, e.Location);

                this.Invalidate(MakeRect(startPt, e.Location, lastLocation));

                lastLocation = e.Location;
            }
        }

        private Rectangle MakeRect(Point pointA, Point pointB, Point pointC)
        {
            int X = Math.Min(Math.Min(pointA.X, pointB.X), pointC.X);
            int Y = Math.Min(Math.Min(pointA.Y, pointB.Y), pointC.Y);
            return new Rectangle(
                X,Y,
                Math.Max(Math.Max(pointA.X, pointB.X), pointC.X) - X, 
                Math.Max(Math.Max(pointA.Y, pointB.Y), pointC.Y) - Y);
        }

    }
}
